importScripts('./opencv.js')

let _cv = null
;(async () => {
  _cv = await cv

  self.postMessage({ type: 'ready' })
  console.log('opencv 已加载完毕！')
})()

function drawCornersOnCanvas(canvas, corners) {
  const ctx = canvas.getContext('2d')
  ctx.fillStyle = 'red'
  ctx.font = '12px sans-serif'
  corners.forEach((p, i) => {
    ctx.beginPath()
    ctx.arc(p.x, p.y, 6, 0, 2 * Math.PI)
    ctx.fill()
    ctx.fillText('P' + i, p.x + 5, p.y - 5)
  })
}

function sortCorners(corners) {
  const center = corners.reduce(
    (a, b) => ({
      x: a.x + b.x / 4,
      y: a.y + b.y / 4
    }),
    { x: 0, y: 0 }
  )

  corners.sort((a, b) => {
    const angleA = Math.atan2(a.y - center.y, a.x - center.x)
    const angleB = Math.atan2(b.y - center.y, b.x - center.x)
    return angleA - angleB
  })

  const minIndex = corners.reduce((minI, pt, i) => (pt.x + pt.y < corners[minI].x + corners[minI].y ? i : minI), 0)

  return [0, 1, 2, 3].map((i) => corners[(minIndex + i) % 4])
}

function varianceOfLaplacian(mat) {
  const gray = new _cv.Mat()
  if (mat.channels() === 4) {
    _cv.cvtColor(mat, gray, _cv.COLOR_RGBA2GRAY)
  } else {
    mat.copyTo(gray)
  }
  const lap = new _cv.Mat()
  _cv.Laplacian(gray, lap, _cv.CV_64F)
  const mean = new _cv.Mat(),
    stddev = new _cv.Mat()
  _cv.meanStdDev(lap, mean, stddev)
  const variance = Math.pow(stddev.doubleAt(0, 0), 2)
  gray.delete()
  lap.delete()
  mean.delete()
  stddev.delete()
  return variance
}

function sobelFocusMeasure(mat) {
  const gray = new _cv.Mat()
  if (mat.channels() === 4) {
    _cv.cvtColor(mat, gray, _cv.COLOR_RGBA2GRAY)
  } else {
    mat.copyTo(gray)
  }
  const gradX = new _cv.Mat(),
    gradY = new _cv.Mat()
  _cv.Sobel(gray, gradX, _cv.CV_64F, 1, 0)
  _cv.Sobel(gray, gradY, _cv.CV_64F, 0, 1)
  const mag = new _cv.Mat()
  _cv.magnitude(gradX, gradY, mag)
  const mean = new _cv.Mat(),
    stddev = new _cv.Mat()
  _cv.meanStdDev(mag, mean, stddev)
  const energy = Math.pow(stddev.doubleAt(0, 0), 2)
  gray.delete()
  gradX.delete()
  gradY.delete()
  mag.delete()
  mean.delete()
  stddev.delete()
  return energy
}

// 处理qrcode
function processQRCode(imageData, corners) {
  const src = _cv.matFromImageData(imageData)

  if (!corners) {
    console.log('无码图')
    src.delete()
    return
  }

  // drawCornersOnCanvas(inputCanvas, corners)

  // 排序角点
  const ordered = sortCorners(corners)
  const width = Math.hypot(ordered[1].x - ordered[0].x, ordered[1].y - ordered[0].y)
  const height = Math.hypot(ordered[3].x - ordered[0].x, ordered[3].y - ordered[0].y)
  const qrSize = Math.floor((width + height) / 2)

  const srcTri = _cv.matFromArray(
    4,
    1,
    _cv.CV_32FC2,
    ordered.flatMap((p) => [p.x, p.y])
  )
  const dstTri = _cv.matFromArray(4, 1, _cv.CV_32FC2, [0, 0, qrSize - 1, 0, qrSize - 1, qrSize - 1, 0, qrSize - 1])

  const M = _cv.getPerspectiveTransform(srcTri, dstTri)
  const corrected = new _cv.Mat()

  try {
    _cv.warpPerspective(src, corrected, M, new _cv.Size(qrSize, qrSize))

    // 清晰度评分
    const lap = varianceOfLaplacian(corrected).toFixed(2)
    const sobel = sobelFocusMeasure(corrected).toFixed(2)

    // 将校正后的图像转换为ImageData可用的格式
    const buffer = new Uint8ClampedArray(corrected.data, corrected.cols * corrected.rows * corrected.channels())
    // 创建副本避免内存问题
    const bufferCopy = new Uint8ClampedArray(buffer)

    return {
      buffer: bufferCopy,
      size: qrSize,
      lap,
      sobel
    }
  } finally {
    src.delete()
    srcTri.delete()
    dstTri.delete()
    M.delete()
    corrected.delete()
  }
}

// 监听主线程发来的消息
self.onmessage = (event) => {
  console.log('Worker 收到消息:', event.data)

  const { id, type, data } = event.data || {}

  switch (type) {
    case 'qecode_usability':
      const { imageData, corners } = data || {}

      console.log('qecode_usability', imageData, corners)

      const result = processQRCode(imageData, corners)

      console.log('qecode_usability 结果:', result, result.buffer instanceof ArrayBuffer)

      self.postMessage({ id, type, data: result })
  }
}
